﻿#pragma once
#include<iostream>
#include<cassert>

template <typename T>
class TemplateVector
{
public:

    TemplateVector();


    TemplateVector(int initialSize);


    ~TemplateVector();


    TemplateVector(const TemplateVector<T>& other);


    TemplateVector<T>& operator=(const TemplateVector<T>& other);


    T& operator[](int index);


    const T& operator[](int index) const;


    void pushBack(T element);


    void removeLast();


    void insert(int index, T element);


    const T& at(int index) const;


    void remove(int index);


    void print() const;


    void clear();


    void resize(int newSize);


    int size() const;

    void setSize(int newSize);

private:

    int _size = 0;

    T* _data = nullptr;

    void copyVector(const TemplateVector<T>& other);
};


template<typename T>
TemplateVector<T>::TemplateVector()
{
}


template<typename T>
TemplateVector<T>::TemplateVector(int initialSize)
{
    resize(initialSize);
}


template<typename T>
TemplateVector<T>::~TemplateVector()
{
    delete[] _data;
}


template<typename T>
TemplateVector<T>::TemplateVector(const TemplateVector<T>& other)
{
    copyVector(other);
}


template<typename T>
TemplateVector<T>& TemplateVector<T>::operator=(const TemplateVector<T>& other)
{
    if (this != &other)
        copyVector(other);

    return *this;
}


template<typename T>
T& TemplateVector<T>::operator[](int index)
{
    assert(index < _size && index >= 0);

    return _data[index];
}


template<typename T>
const T& TemplateVector<T>:: operator[](int index) const
{
    assert(index <= _size || index < 0);

    return _data[index];
}


template<typename T>
void TemplateVector<T>::pushBack(T element)
{
    resize(_size + 1);

    _data[_size - 1] = element;
}


template<typename T>
void TemplateVector<T>::removeLast()
{
    assert(_size > 0);

    resize(_size - 1);
}


template<typename T>
void TemplateVector<T>::insert(int index, T element)
{
    assert(index >= 0);

    resize(std::max(index + 1, _size + 1));

    for (int i = _size - 1; i > index; --i)
        _data[i] = _data[i - 1]; // move elements --->

    _data[index] = element;
}


template<typename T>
const T& TemplateVector<T>::at(int index) const
{
    return (*this)[index];
}


template<typename T>
void TemplateVector<T>::remove(int index)
{
    assert(index < _size && index >= 0);

    for (int i = index; i < _size - 1; ++i)
        _data[i] = _data[i + 1]; //move elements <---

    --_size;
}


template<typename T>
void TemplateVector<T>::print() const
{
    if (_size == 0 && _data == nullptr)
        std::cout << "empty vector" << std::endl << std::endl;
    else
    {
        for (int i = 0; i < _size; ++i)
            std::cout << _data[i] << " ";

        std::cout << std::endl;
    }

    std::cout << std::endl << std::endl;
}


template<typename T>
void TemplateVector<T>::clear()
{
    delete[] _data;

    _size = 0;

    _data = nullptr;
}


template<typename T>
void TemplateVector<T>::resize(int newSize)
{
    assert(newSize >= 0);

    if (newSize == 0)
    {
        this->clear();

        return; // выходим из функции, если ресайз на 0, тк копировать нечего будет
    }

    T* newData = new T[newSize];

    int countItemsCopy = std::min(_size, newSize);

    if (_data != nullptr)
    {
        for (int i = 0; i < countItemsCopy; i++)
            newData[i] = _data[i];

        delete[] _data;

        _data = newData;
    }
    else
        _data = newData;

    _size = newSize;
}


template<typename T>
int TemplateVector<T>::size() const
{
    return _size;
}

template<typename T>
inline void TemplateVector<T>::setSize(int newSize)
{
    _size = newSize;
}


template<typename T>
void TemplateVector<T>::copyVector(const TemplateVector<T>& other)
{
    if (_data != nullptr)
        delete[] _data;

    _data = new T[other._size];

    _size = other._size;

    for (int i = 0; i < _size; i++)
        _data[i] = other._data[i];
}